#include <dspSimulator/instruction/addaw.h>


bool ADDAW::checkCondition(std::shared_ptr<DspMachine> dspMachine) {
    if (isCompactInstruction) {
        return true;
    } else {
        return checkStandardCondition(dspMachine);
    }
}

void ADDAW::loadInstIRIntoJit(llvm::LLVMContext *llvmContext, llvm::Module *module,
                              std::shared_ptr<llvm::IRBuilder<>> irBuilder) {

}

std::shared_ptr<Instruction> ADDAW::decode(std::shared_ptr<InstInfo> instInfo, u64 baseCycle) {
    std::shared_ptr<ADDAW> addaw;
    auto dspInstInfo = std::dynamic_pointer_cast<DspInstInfo>(instInfo);
    bool isStandardInst = dspInstInfo->isStandardInstruction();
    if (isStandardInst) {
        auto data = vectorToBitSet<32>(dspInstInfo->getBits());
        if (checkBits<11>(data, 2, 0b11100010000) && existStandardCondition(data)) {
            addaw = std::make_shared<ADDAW>(dspInstInfo->getAddress(), baseCycle);
            addaw->creg_z = extractBits<4>(data, 28);
            addaw->dst = extractBits<5>(data, 23).to_ulong();
            addaw->src2 = extractBits<5>(data, 18).to_ulong();
            addaw->src1 = extractBits<5>(data, 13).to_ulong();
            addaw->s = data[1];
            addaw->p = data[0];
            if (addaw->s == 0) {
                addaw->funcUnit = FuncUnit::D1;
            } else {
                addaw->funcUnit = FuncUnit::D1;
            }
            addaw->instFormat = InstFormat::Kind1;
        } else if (checkBits<11>(data, 2, 0b11101010000) && existStandardCondition(data)) {
            addaw = std::make_shared<ADDAW>(dspInstInfo->getAddress(), baseCycle);
            addaw->creg_z = extractBits<4>(data, 28);
            addaw->dst = extractBits<5>(data, 23).to_ulong();
            addaw->src2 = extractBits<5>(data, 18).to_ulong();
            addaw->src1 = extractBits<5>(data, 13).to_ulong();
            addaw->s = data[1];
            addaw->p = data[0];
            if (addaw->s == 0) {
                addaw->funcUnit = FuncUnit::D1;
            } else {
                addaw->funcUnit = FuncUnit::D1;
            }
            addaw->instFormat = InstFormat::Kind2;
        } else if (checkBits<4>(data, 28, 0b0001) && checkBits<5>(data, 2, 0b11111)) {
            addaw = std::make_shared<ADDAW>(dspInstInfo->getAddress(), baseCycle);
            addaw->dst = extractBits<5>(data, 23).to_ulong();
            addaw->cst = extractBits<15>(data, 8).to_ulong();
            addaw->y = data[7];
            addaw->s = data[1];
            addaw->p = data[0];
            if (addaw->s == 0) {
                addaw->funcUnit = FuncUnit::D1;
            } else {
                addaw->funcUnit = FuncUnit::D2;
            }
            addaw->instFormat = InstFormat::Kind3;
        }
    } else {
        auto data = vectorToBitSet<16>(dspInstInfo->getBits());
        if (data[10] == 1 && checkBits<6>(data, 1, 0b011011)) {
            addaw = std::make_shared<ADDAW>(dspInstInfo->getAddress(), baseCycle);
            addaw->isCompactInstruction = true;
            addaw->compactInstKind = "Dx5";
            addaw->dst = extractBits<3>(data, 7).to_ulong();
            addaw->cst = extractBits<3>(data, 13).to_ulong();
            addaw->cst += extractBits<2>(data, 11).to_ulong() << 3;
            addaw->y = 1;
            addaw->s = data[0];
            addaw->RS = dspInstInfo->getRS();
            addaw->p = dspInstInfo->getP();
            if (addaw->s == 0) {
                addaw->funcUnit = FuncUnit::D1;
            } else {
                addaw->funcUnit = FuncUnit::D2;
            }
            addaw->PROT = dspInstInfo->getPROT();
            addaw->SAT = dspInstInfo->getSAT();
            addaw->BR = dspInstInfo->getBR();
            addaw->RS = dspInstInfo->getRS();
            addaw->DSZ = dspInstInfo->getDSZ();
            addaw->p = dspInstInfo->getP();
            addaw->fixUpRegOnCompactInstruction();
            addaw->instFormat = InstFormat::Kind3;
        }
        if (checkBits<3>(data, 10, 0b011) && checkBits<8>(data, 0, 0b01110111)) {
            addaw = std::make_shared<ADDAW>(dspInstInfo->getAddress(), baseCycle);
            addaw->isCompactInstruction = true;
            addaw->compactInstKind = "Dx5p";
            addaw->dst = addaw->src2 = 15;
            addaw->cst = extractBits<3>(data, 13).to_ulong();
            addaw->cst += extractBits<2>(data, 8).to_ulong() << 3;
            addaw->funcUnit = FuncUnit::D2;
            addaw->PROT = dspInstInfo->getPROT();
            addaw->SAT = dspInstInfo->getSAT();
            addaw->BR = dspInstInfo->getBR();
            addaw->RS = dspInstInfo->getRS();
            addaw->DSZ = dspInstInfo->getDSZ();
            addaw->p = dspInstInfo->getP();
            addaw->fixUpRegOnCompactInstruction();
            addaw->instFormat = InstFormat::Kind2;
        }
    }
    return addaw;
}

void ADDAW::executeCustom(std::shared_ptr<TargetMachine> targetMachine, ExecutePhase executePhase) {
    u32 op2 = 0;
    auto dspMachine = std::dynamic_pointer_cast<DspMachine>(targetMachine);
    if (instFormat == InstFormat::Kind1) {
        // ADDAW (.unit) src2, src1, dst
        op2 = *src1LoRegPtr << 2;
    } else if (instFormat == InstFormat::Kind2) {
        // ADDAW (.unit) src2, ucst5, dst
        op2 = src1 << 2;
    } else if (instFormat == InstFormat::Kind3) {
        // ADDAW (.unit) B14/B15, ucst15, dst
        op2 = cst << 2;
    }

    if (instFormat == InstFormat::Kind1 || instFormat == InstFormat::Kind2) {
        if (src2 >= 4 && src2 <= 7) {
            if (s == 0) {
                auto adddrMode = dspMachine->getRegAAddressingMode(src2);
                if (adddrMode == AddressingMode::Linear) {
                    *dstLoRegPtr = *src2LoRegPtr + op2;
                } else if (adddrMode == AddressingMode::CircularBK0) {
                    auto blockSize = dspMachine->getAMRBlockSize(0);
                    *dstLoRegPtr = *src2LoRegPtr + op2 % blockSize;
                } else if (adddrMode == AddressingMode::CircularBK1) {
                    auto blockSize = dspMachine->getAMRBlockSize(1);
                    *dstLoRegPtr = *src2LoRegPtr + op2 % blockSize;
                }
            } else if (s == 1) {
                auto adddrMode = dspMachine->getRegBAddressingMode(src2);
                if (adddrMode == AddressingMode::Linear) {
                    *dstLoRegPtr = *src2LoRegPtr + op2;
                } else if (adddrMode == AddressingMode::CircularBK0) {
                    auto blockSize = dspMachine->getAMRBlockSize(0);
                    *dstLoRegPtr = *src2LoRegPtr + op2 % blockSize;
                } else if (adddrMode == AddressingMode::CircularBK1) {
                    auto blockSize = dspMachine->getAMRBlockSize(1);
                    *dstLoRegPtr = *src2LoRegPtr + op2 % blockSize;
                }
            }
        } else {
            *dstLoRegPtr = *src2LoRegPtr + op2;
        }
    } else if (instFormat == InstFormat::Kind3) {
        if (y == 0) {
            auto b14 = *dspMachine->getSaveRegs()->getReg32ByName("B14")->getDataPtr();
            *dstLoRegPtr = b14 + (cst << 2);
        } else {
            auto b15 = *dspMachine->getSaveRegs()->getReg32ByName("B15")->getDataPtr();
            *dstLoRegPtr = b15 + (cst << 2);
        }
    }
}

std::string ADDAW::toString() const {
    std::string res;
    if (!isCompactInstruction) {
        res += getStandardConditionString() + " ";
    }
    res += m_name + " " + FuncUnitName[static_cast<u32>(funcUnit)] + " ";
    if (instFormat == InstFormat::Kind1) {
        res += getSrc2Reg32Name() + "," + getSrc1Reg32Name() + "," + getDstReg32Name();
    } else if (instFormat == InstFormat::Kind2) {
        res += getSrc2Reg32Name() + "," + std::to_string(src1) + "," + getDstReg32Name();
    } else if (instFormat == InstFormat::Kind3) {
        if (y == 0) {
            res += "B14," + std::to_string(cst) + "," + getDstReg32Name();
        } else {
            res += "B15," + std::to_string(cst) + "," + getDstReg32Name();
        }
    }
    return res;
}

std::shared_ptr<DspInstruction> ADDAW::clone() {
    return std::make_shared<ADDAW>(*this);
}
